package logo.lang;

public abstract class LogoNum extends LogoVal {

    protected LogoNum(){}

    public boolean isNum() {
	return true;
    }

    public boolean isInt() {
	return false;
    }

    public boolean isDouble() {
	return false;
    }

    public LogoNum numValue() {
	return this;
    }

    public LogoBool eq(LogoVal v2) throws LogoException {
	if (v2.isNum())
	    if (doubleValue() == v2.doubleValue())
		return TRUE;
	return FALSE;
    }

    public LogoBool lt(LogoNum v2) throws LogoException {
	if (v2.isNum())
	    if (doubleValue() < v2.doubleValue())
		return TRUE;
	return FALSE;
    }

    public LogoBool gt(LogoNum v2) throws LogoException {
	if (v2.isNum())
	    if (doubleValue() > v2.doubleValue())
		return TRUE;
	return FALSE;
    }

    public abstract LogoNum add(LogoNum val) throws LogoWrongTypeException;

    public abstract LogoNum sub(LogoNum val) throws LogoWrongTypeException;

    public abstract LogoNum mul(LogoNum val) throws LogoWrongTypeException;

    public abstract LogoNum expt(LogoNum val) throws LogoWrongTypeException;

    public abstract LogoNum div(LogoNum val) throws LogoWrongTypeException;

    public abstract LogoNum mod(LogoNum val) throws LogoWrongTypeException;

    /*
    public LogoNum add(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of add! " +
			this + " Get a wizard!");
    }

    public LogoNum sub(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of sub! " +
			this + " Get a wizard!");
    }

    public LogoNum mul(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of mul! " +
			this + " Get a wizard!");
    }

    public LogoNum expt(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of expt! " +
			this + " Get a wizard!");
    }

    public LogoNum div(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of div! " +
			this + " Get a wizard!");
    }

    public LogoNum mod(LogoNum val) throws LogoWrongTypeException {
	throw new Error("System Error: Number has no implementation of mod! " +
			this + " Get a wizard!");
    }
    */
    /* Internal subclasses */

    public static class Int extends LogoNum {
	int val;

	public Int(int v) {
	    val = v;
	}

	public boolean isInt() {
	    return true;
	}

	public double doubleValue() {
	    return (double)val;
	}

	public int intValue() {
	    return val;
	}
	
	/*

	public LogoNum add(LogoNum.Int v) throws LogoWrongTypeException {
	    return new LogoNum.Int(val + v.intValue());
	}

	public LogoNum add(LogoNum.Double v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val + v.doubleValue());
	}

	public LogoNum sub(LogoNum.Int v) throws LogoWrongTypeException {
	    return new LogoNum.Int(val - v.intValue());
	}

	public LogoNum sub(LogoNum.Double v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val - v.doubleValue());
	}

	public LogoNum mul(LogoNum.Int v) throws LogoWrongTypeException {
	    return new LogoNum.Int(val * v.intValue());
	}

	public LogoNum mul(LogoNum.Double v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val * v.doubleValue());
	}

	public LogoNum div(LogoNum.Int v) throws LogoWrongTypeException {
	    return new LogoNum.Int(val / v.intValue());
	}

	public LogoNum div(LogoNum.Double v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val / v.doubleValue());
	}

	public LogoNum mod(LogoNum.Int v) throws LogoWrongTypeException {
	    return new LogoNum.Int(val % v.intValue());
	}

	public LogoNum mod(LogoNum.Double v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val % v.doubleValue());
	}
	*/

	public LogoNum add(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt())
		return new LogoNum.Int(val + v.intValue());
	    else if (v.isDouble())
		return new LogoNum.Double(val + v.doubleValue());
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}

	public LogoNum sub(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt())
		return new LogoNum.Int(val - v.intValue());
	    else if (v.isDouble())
		return new LogoNum.Double(val - v.doubleValue());
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}

	public LogoNum mul(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt())
		return new LogoNum.Int(val * v.intValue());
	    else if (v.isDouble())
		return new LogoNum.Double(val * v.doubleValue());
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}

	public LogoNum expt(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt())
		return new LogoNum.Int((int)Math.pow(val, v.intValue()));
	    else if (v.isDouble())
		return new LogoNum.Double(Math.pow(val, v.doubleValue()));
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}

	public LogoNum mod(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt())
		return new LogoNum.Int(val % v.intValue());
	    else if (v.isDouble())
		return new LogoNum.Double(val % v.doubleValue());
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}

	public LogoNum div(LogoNum v) throws LogoWrongTypeException {
	    if (v.isInt()) {
		int argVal = v.intValue();
		int result = val / argVal;
		if (result * argVal == val)
		    return new LogoNum.Int(result);
		else
		    return new LogoNum.Double(val / v.doubleValue());
	    } else if (v.isDouble())
		return new LogoNum.Double(val % v.doubleValue());
	    else
		throw new Error("System error: number " + v +
				" is neither integer nor double! " +
				"Get help!");
	}
    }

    public static class Double extends LogoNum {
	double val;

	public Double(double v) {
	    val = v;
	}

	public boolean isDouble() {
	    return true;
	}

	public double doubleValue() {
	    return val;
	}

	public int intValue() {
	    return (int) val;
	}

	public LogoNum add(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val + v.doubleValue());
	}

	public LogoNum sub(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val - v.doubleValue());
	}

	public LogoNum mul(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val * v.doubleValue());
	}

	public LogoNum div(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val / v.doubleValue());
	}

	public LogoNum mod(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(val % v.doubleValue());
	}

	public LogoNum expt(LogoNum v) throws LogoWrongTypeException {
	    return new LogoNum.Double(Math.pow(val, v.doubleValue()));
	}

    }
}
